package net.skycloud.f5.service;

import net.skycloud.f5.exception.DatabaseOperationException;
import net.skycloud.f5.exception.ObjectConflictException;
import net.skycloud.f5.exception.ObjectNotFoundException;
import net.skycloud.f5.exception.ParamCheckFailedException;
import net.skycloud.f5.model.CredentialType;
import net.skycloud.f5.model.DeviceCredential;
import net.skycloud.f5.repository.DeviceCredentialRepository;
import net.skycloud.f5.util.PageInfo;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * Created by ayang on 2017/7/28.
 */
@Service
public class DeviceCredentialService {

    private static final Logger logger = LoggerFactory.getLogger(DeviceCredentialService.class);

    @Autowired
    private DeviceCredentialRepository credentialRepository;

    public DeviceCredential add(DeviceCredential nCred) throws Exception {
        DeviceCredential res = null;

        try {
            res = credentialRepository.findByName(nCred.getName());
            if (res != null) {
                throw new ObjectConflictException(String.format("DeviceCredential [%s] already exist.", nCred.getName()));
            }

            JSONObject value = new JSONObject(nCred.getData());
            String username = value.getString("username");
            String password = value.getString("password");
            if(null == username || username.trim().isEmpty() || null == password || password.trim().isEmpty()) {
                throw new ObjectConflictException(String.format("DeviceCredential [%s] username or password error",nCred.getName()));
            }
            nCred.setData(value.toString());

            res = credentialRepository.save(nCred);
        } catch (ObjectConflictException e) {
            throw e;
        } catch (Exception e) {
            logger.error(e.getMessage());
            throw new DatabaseOperationException(e.getMessage());
        }

        return res;
    }

    public void deleteById(UUID id) throws Exception {
        DeviceCredential res = null;

        try {
            res = credentialRepository.findOne(id);
            if (res == null) {
                throw new ObjectNotFoundException("No such device credential.");
            }
            credentialRepository.delete(id);
        } catch (ObjectNotFoundException e) {
            throw e;
        } catch (Exception e) {
            logger.error(e.getMessage());
            throw new DatabaseOperationException(e.getMessage());
        }

    }

    public DeviceCredential getCredentialById(UUID id) throws Exception {
        DeviceCredential res = null;

        try {
            res = credentialRepository.findOne(id);
        } catch (Exception e) {
            logger.error(e.getMessage());
            throw new DatabaseOperationException(e.getMessage());
        }

        return res;
    }

    public List<DeviceCredential> getAll() throws Exception {
        List<DeviceCredential> credentials = null;

        try {
            credentials = credentialRepository.findAll();
        } catch (Exception e) {
            logger.error(e.getMessage());
            throw new DatabaseOperationException(e.getMessage());
        }

        if (credentials == null) {
            return new ArrayList<DeviceCredential>();
        } else {
            return credentials;
        }
    }

    public PageInfo<DeviceCredential> getAll(Integer page, Integer size) throws Exception {
        page = page < 0 ? 0 : page;
        size = size < 1 ? 1 : size;
        Sort sort = new Sort(Sort.Direction.DESC, "createdAt");
        Pageable pageable = new PageRequest(page, size, sort);
        Page<DeviceCredential> credentialPage = credentialRepository.findAll(pageable);
        return new PageInfo<>(credentialPage);
    }

    public DeviceCredential update(DeviceCredential nCred) throws Exception {
        DeviceCredential current = null;

        try {
            current = credentialRepository.findOne(nCred.getId());
            if (current == null) {
                throw new ObjectNotFoundException("No such device credential");
            }
            // name
            if (nCred.getName() != null && !nCred.equals(current.getName())) {
                if (nCred.getName().isEmpty()) {
                    throw new ParamCheckFailedException("The name of credential can not be empty.");
                }
                DeviceCredential deviceCredential = credentialRepository.findByName(nCred.getName());
                if (deviceCredential != null && !deviceCredential.getId().equals(current.getId())) {
                    throw new ParamCheckFailedException("The name of credential already exist, please try another one");
                }
                current.setName(nCred.getName());
            }
            // description
            if (nCred.getDescription() != null) {
                current.setDescription(nCred.getDescription());
            }
            // type & value
            if (nCred.getType() != null) {
                if (nCred.getData() == null) {
                    throw new ParamCheckFailedException("Type and value should be change simultaneously.");
                }
                if (nCred.getType().equals(CredentialType.USERNAME_PASSWORD)) {
                    // TODO: check username and password not null
                } else {
                    throw new ParamCheckFailedException("Type and value are illegal.");
                }
                current.setType(nCred.getType());
                current.setData(nCred.getData());
                current.setUpdatedAt(new Date());
            }
            return credentialRepository.save(current);
        } catch (ObjectNotFoundException e) {
            throw e;
        } catch (ParamCheckFailedException e) {
            throw e;
        } catch (Exception e) {
            logger.error(e.getMessage());
            throw new DatabaseOperationException(e.getMessage());
        }
    }

    public DeviceCredential isExist(DeviceCredential cred) throws Exception {
        DeviceCredential res = null;

        try {
            res = credentialRepository.findOne(cred.getId());
            if (res == null) {
                return null;
            }
            if (cred.getName() != null && !res.getName().equals(cred.getName())) {
                return null;
            }
            return res;
        } catch (Exception e) {
            logger.error(e.getMessage());
            throw new DatabaseOperationException(e.getMessage());
        }
    }
}
